/**
 * @file scd4x_i2c_optimized.cc
 * @brief 优化的SCD4X传感器驱动类实现，包含专用I2C工具函数
 * @author 嵌入式系统工程师
 * @version 1.0
 * @date 2024-05-20
 */

#include "scd4x_i2c_optimized.h"
#include <cstring>

// 日志标签
static const char* TAG = "SCD4X_OPTIMIZED";

SCD4X::SCD4X(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
    // 调用父类构造函数初始化I2C设备
    // 不需要重复初始化i2c_device_
    ESP_LOGI(TAG, "SCD4X sensor initialized on I2C address 0x%02X", addr);
}

SCD4X::~SCD4X() {
    // 父类会负责清理i2c_device_
    ESP_LOGI(TAG, "SCD4X sensor destroyed");
}

void SCD4X::begin() {
    // 唤醒传感器
    setSleepMode(false);
    vTaskDelay(pdMS_TO_TICKS(100)); // 等待传感器稳定
    
    // 尝试停止任何正在进行的测量
    sendCommand(SCD4X_STOP_PERIODIC_MEASURE, DEFAULT_TIMEOUT_MS, 1); // 只尝试一次
    vTaskDelay(pdMS_TO_TICKS(50));
    
    // 启动周期性测量
    enablePeriodMeasure(SCD4X_MODE_PERIODIC);
    
    ESP_LOGI(TAG, "SCD4X sensor initialized successfully");
}

bool SCD4X::readMeasurement(Measurement* data) {
    if (!data) {
        ESP_LOGE(TAG, "Invalid data pointer");
        return false;
    }
    
    // 缓冲区用于存储原始数据（6字节数据 + 3字节CRC）
    uint8_t raw_data[9];
    
    // 发送读取命令并接收数据
    esp_err_t result = sendCommandAndRead(SCD4X_READ_MEASUREMENT, raw_data, sizeof(raw_data));
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read measurement data: %d", result);
        return false;
    }
    
    // 验证CRC校验
    for (int i = 0; i < 3; i++) {
        if (!verifyCRC(&raw_data[i * 3], 2)) {
            ESP_LOGE(TAG, "CRC verification failed for measurement data segment %d", i);
            return false;
        }
    }
    
    // 解析数据
    data->co2_ppm = bigEndianToUint16(&raw_data[0]);
    
    // 温度转换：Temperature [°C] = -45 + 175 * (raw_value / 2^16)
    uint16_t raw_temp = bigEndianToUint16(&raw_data[3]);
    data->temperature = -45.0f + (175.0f * raw_temp) / 65536.0f;
    
    // 湿度转换：Humidity [%RH] = 100 * (raw_value / 2^16)
    uint16_t raw_hum = bigEndianToUint16(&raw_data[6]);
    data->humidity = (100.0f * raw_hum) / 65536.0f;
    
    return true;
}

bool SCD4X::getDataReadyStatus() {
    uint8_t raw_data[3];
    
    // 发送读取就绪状态命令并接收数据
    esp_err_t result = sendCommandAndRead(SCD4X_GET_DATA_READY_STATUS, raw_data, sizeof(raw_data));
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read data ready status: %d", result);
        return false;
    }
    
    // 验证CRC校验
    if (!verifyCRC(raw_data, 2)) {
        ESP_LOGE(TAG, "CRC verification failed for data ready status");
        return false;
    }
    
    // 数据就绪状态在最低位，检查第15位是否为1
    uint16_t status = bigEndianToUint16(raw_data);
    return ((status & 0x0001) == 0x0001);
}

void SCD4X::setSleepMode(bool sleep) {
    if (!sleep) {
        // 唤醒命令需要特殊处理
        uint8_t wakeup_cmd[2];
        uint16ToBigEndian(SCD4X_WAKE_UP, wakeup_cmd);
        
        // 唤醒命令使用直接I2C传输
        esp_err_t result = i2c_master_transmit(i2c_device_, wakeup_cmd, sizeof(wakeup_cmd), DEFAULT_TIMEOUT_MS);
        if (result != ESP_OK) {
            ESP_LOGE(TAG, "Failed to wake up sensor: %d", result);
        } else {
            vTaskDelay(pdMS_TO_TICKS(20)); // 唤醒后需要20ms延迟
        }
    } else {
        // 睡眠模式使用标准命令发送
        sendCommand(SCD4X_POWER_DOWN, DEFAULT_TIMEOUT_MS);
    }
}

void SCD4X::enablePeriodMeasure(uint8_t mode) {
    uint16_t cmd;
    
    if (mode == SCD4X_MODE_PERIODIC) {
        cmd = SCD4X_START_PERIODIC_MEASURE;
        ESP_LOGI(TAG, "Starting periodic measurement mode");
    } else if (mode == SCD4X_MODE_LOW_POWER) {
        cmd = SCD4X_START_LOW_POWER_MEASURE;
        ESP_LOGI(TAG, "Starting low power measurement mode");
    } else {
        cmd = SCD4X_STOP_PERIODIC_MEASURE;
        ESP_LOGI(TAG, "Stopping periodic measurement");
    }
    
    sendCommand(cmd, DEFAULT_TIMEOUT_MS, 3);
}

bool SCD4X::measureSingleShot(bool rht_only) {
    uint16_t cmd = rht_only ? SCD4X_MEASURE_SINGLE_SHOT_RHT_ONLY : SCD4X_MEASURE_SINGLE_SHOT;
    
    esp_err_t result = sendCommand(cmd);
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to start single shot measurement: %d", result);
        return false;
    }
    
    return true;
}

uint16_t SCD4X::performSelfTest() {
    // 发送自检命令
    esp_err_t result = sendCommand(SCD4X_PERFORM_SELF_TEST);
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to start self-test: %d", result);
        return 0xFFFF; // 错误标志
    }
    
    // 自检需要约10秒完成
    ESP_LOGI(TAG, "Self-test started, waiting 10 seconds...");
    vTaskDelay(pdMS_TO_TICKS(10000));
    
    // 读取自检结果（使用读取测量命令，自检结果通过该命令返回）
    uint8_t raw_data[3];
    result = sendCommandAndRead(SCD4X_READ_MEASUREMENT, raw_data, sizeof(raw_data));
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read self-test result: %d", result);
        return 0xFFFF;
    }
    
    // 验证CRC校验
    if (!verifyCRC(raw_data, 2)) {
        ESP_LOGE(TAG, "CRC verification failed for self-test result");
        return 0xFFFF;
    }
    
    // 返回自检结果
    uint16_t test_result = bigEndianToUint16(raw_data);
    ESP_LOGI(TAG, "Self-test result: 0x%04X", test_result);
    
    return test_result;
}

bool SCD4X::setTemperatureOffset(float temp_offset) {
    // 将温度偏移值转换为传感器需要的格式
    // 温度偏移单位为0.01°C
    uint16_t temp_offset_int = (uint16_t)((temp_offset * 100) + 0.5);
    
    // 发送带参数的命令
    esp_err_t result = sendCommandWithParam(SCD4X_SET_TEMPERATURE_OFFSET, temp_offset_int);
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to set temperature offset: %d", result);
        return false;
    }
    
    ESP_LOGI(TAG, "Temperature offset set to %.2f °C", temp_offset);
    return true;
}

// ===== 以下是添加的功能函数 =====

void SCD4X::setTempComp(float temp_comp) {
    // 将温度偏移值转换为传感器需要的格式
    // 温度偏移单位为0.01°C
    uint16_t temp_comp_int = (uint16_t)((temp_comp * 100) + 0.5);
    
    // 发送带参数的命令
    sendCommandWithParam(SCD4X_SET_TEMPERATURE_OFFSET, temp_comp_int, DEFAULT_TIMEOUT_MS, 3);
    
    // 更新本地缓存的温度偏移值
    temp_comp_ = temp_comp;
    
    ESP_LOGI(TAG, "Temperature compensation set to %.2f °C", temp_comp);
}

float SCD4X::getTempComp() {
    // 先从传感器读取当前温度偏移值
    uint8_t raw_data[3];
    esp_err_t result = sendCommandAndRead(SCD4X_GET_TEMPERATURE_OFFSET, raw_data, sizeof(raw_data));
    
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read temperature offset from sensor: %d", result);
        return temp_comp_; // 返回本地缓存值
    }
    
    // 验证CRC校验
    if (!verifyCRC(raw_data, 2)) {
        ESP_LOGE(TAG, "CRC verification failed for temperature offset");
        return temp_comp_; // 返回本地缓存值
    }
    
    // 解析并更新本地缓存
    uint16_t temp_offset_int = bigEndianToUint16(raw_data);
    temp_comp_ = (float)temp_offset_int / 100.0f;
    
    ESP_LOGI(TAG, "Retrieved temperature compensation: %.2f °C", temp_comp_);
    return temp_comp_;
}

void SCD4X::setSensorAltitude(uint16_t altitude) {
    // 发送带参数的命令
    sendCommandWithParam(SCD4X_SET_SENSOR_ALTITUDE, altitude, DEFAULT_TIMEOUT_MS, 3);
    
    // 更新本地缓存的海拔高度
    sensor_altitude_ = altitude;
    
    ESP_LOGI(TAG, "Sensor altitude set to %d meters", altitude);
}

uint16_t SCD4X::getSensorAltitude() {
    // 从传感器读取当前海拔高度
    uint8_t raw_data[3];
    esp_err_t result = sendCommandAndRead(SCD4X_GET_SENSOR_ALTITUDE, raw_data, sizeof(raw_data));
    
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read sensor altitude: %d", result);
        return sensor_altitude_; // 返回本地缓存值
    }
    
    // 验证CRC校验
    if (!verifyCRC(raw_data, 2)) {
        ESP_LOGE(TAG, "CRC verification failed for sensor altitude");
        return sensor_altitude_; // 返回本地缓存值
    }
    
    // 解析并更新本地缓存
    sensor_altitude_ = bigEndianToUint16(raw_data);
    
    ESP_LOGI(TAG, "Retrieved sensor altitude: %d meters", sensor_altitude_);
    return sensor_altitude_;
}

void SCD4X::setAmbientPressure(uint32_t ambient_pressure) {
    // 环境气压参数是24位的，但我们需要将其右移8位作为16位参数
    // 气压单位：百帕
    uint16_t pressure_param = (uint16_t)(ambient_pressure >> 8);
    
    // 发送带参数的命令
    sendCommandWithParam(SCD4X_SET_AMBIENT_PRESSURE, pressure_param, DEFAULT_TIMEOUT_MS, 3);
    
    ESP_LOGI(TAG, "Ambient pressure set to %u hPa", ambient_pressure);
}

void SCD4X::setAutoCalibMode(bool enable) {
    // 将启用/禁用状态转换为参数（0或1）
    uint16_t param = enable ? 1 : 0;
    
    // 发送带参数的命令
    sendCommandWithParam(SCD4X_SET_AUTOMATIC_CALIB, param, DEFAULT_TIMEOUT_MS, 3);
    
    ESP_LOGI(TAG, "Automatic calibration %s", enable ? "enabled" : "disabled");
}

bool SCD4X::getAutoCalibMode() {
    // 从传感器读取自动校准模式状态
    uint8_t raw_data[3];
    esp_err_t result = sendCommandAndRead(SCD4X_GET_AUTOMATIC_CALIB, raw_data, sizeof(raw_data));
    
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read auto-calibration mode: %d", result);
        return false;
    }
    
    // 验证CRC校验
    if (!verifyCRC(raw_data, 2)) {
        ESP_LOGE(TAG, "CRC verification failed for auto-calibration mode");
        return false;
    }
    
    // 解析结果
    uint16_t status = bigEndianToUint16(raw_data);
    bool enabled = (status == 1);
    
    ESP_LOGI(TAG, "Retrieved auto-calibration mode: %s", enabled ? "enabled" : "disabled");
    return enabled;
}

void SCD4X::persistSettings() {
    // 发送持久化设置命令
    sendCommand(SCD4X_PERSIST_SETTINGS, DEFAULT_TIMEOUT_MS, 3);
    
    // 持久化设置需要约800ms
    vTaskDelay(pdMS_TO_TICKS(800));
    
    ESP_LOGI(TAG, "Settings persisted to sensor EEPROM");
}

bool SCD4X::getSerialNumber(uint16_t serial[3]) {
    if (!serial) {
        ESP_LOGE(TAG, "Invalid serial buffer pointer");
        return false;
    }
    
    // 缓冲区用于存储原始数据（6字节数据 + 3字节CRC）
    uint8_t raw_data[9];
    
    // 发送读取序列号命令并接收数据
    esp_err_t result = sendCommandAndRead(SCD4X_GET_SERIAL_NUMBER, raw_data, sizeof(raw_data));
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read serial number: %d", result);
        return false;
    }
    
    // 验证CRC校验
    for (int i = 0; i < 3; i++) {
        if (!verifyCRC(&raw_data[i * 3], 2)) {
            ESP_LOGE(TAG, "CRC verification failed for serial number segment %d", i);
            return false;
        }
    }
    
    // 解析序列号
    serial[0] = bigEndianToUint16(&raw_data[0]);
    serial[1] = bigEndianToUint16(&raw_data[3]);
    serial[2] = bigEndianToUint16(&raw_data[6]);
    
    ESP_LOGI(TAG, "Retrieved serial number: 0x%04X 0x%04X 0x%04X", serial[0], serial[1], serial[2]);
    return true;
}

int16_t SCD4X::performForcedRecalibration(uint16_t co2_ppm) {
    // 发送强制校准命令，参数为参考CO2浓度
    sendCommandWithParam(SCD4X_PERFORM_FORCED_RECALIB, co2_ppm, DEFAULT_TIMEOUT_MS, 3);
    
    // 强制校准需要约400ms
    vTaskDelay(pdMS_TO_TICKS(400));
    
    // 读取校准结果
    uint8_t raw_data[3];
    esp_err_t result = sendCommandAndRead(SCD4X_READ_MEASUREMENT, raw_data, sizeof(raw_data));
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "Failed to read forced recalibration result: %d", result);
        return -1;
    }
    
    // 验证CRC校验
    if (!verifyCRC(raw_data, 2)) {
        ESP_LOGE(TAG, "CRC verification failed for forced recalibration result");
        return -1;
    }
    
    // 解析校准值（有符号16位整数）
    uint16_t raw_calib_val = bigEndianToUint16(raw_data);
    int16_t calib_val = (int16_t)raw_calib_val;
    
    // 检查校准是否成功（如果校准值在-1000到1000之间，则成功）
    if (calib_val < -1000 || calib_val > 1000) {
        ESP_LOGE(TAG, "Forced recalibration failed, returned value: %d", calib_val);
        return -1;
    }
    
    ESP_LOGI(TAG, "Forced recalibration completed, calibration value: %d", calib_val);
    return calib_val;
}

void SCD4X::moduleReinit() {
    // 发送重置命令
    sendCommand(SCD4X_REINIT, DEFAULT_TIMEOUT_MS, 3);
    
    // 重置需要约20ms
    vTaskDelay(pdMS_TO_TICKS(20));
    
    ESP_LOGI(TAG, "Sensor module reinitialized");
}

void SCD4X::performFactoryReset() {
    // 发送恢复出厂设置命令
    sendCommand(SCD4X_PERFORM_FACTORY_RESET, DEFAULT_TIMEOUT_MS, 3);
    
    // 恢复出厂设置需要约1200ms
    vTaskDelay(pdMS_TO_TICKS(1200));
    
    ESP_LOGI(TAG, "Sensor factory reset completed");
}

// ===== I2C专用工具函数 =====

esp_err_t SCD4X::sendCommand(uint16_t command, uint32_t timeout, uint8_t max_retries) {
    uint8_t tx_buffer[2];
    uint16ToBigEndian(command, tx_buffer);
    
    esp_err_t result;
    for (uint8_t retry = 0; retry < max_retries; retry++) {
        result = i2c_master_transmit(i2c_device_, tx_buffer, sizeof(tx_buffer), timeout);
        if (result == ESP_OK) {
            return ESP_OK;
        }
        
        ESP_LOGE(TAG, "I2C transmit failed, retry %d/%d: %d", retry + 1, max_retries, result);
        // 重试前短暂延迟
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    
    return result; // 返回最后一次错误
}

esp_err_t SCD4X::sendCommandWithParam(uint16_t command, uint16_t param, uint32_t timeout, uint8_t max_retries) {
    uint8_t tx_buffer[5]; // 2字节命令 + 2字节参数 + 1字节CRC
    
    // 将命令和参数转换为大端序
    uint16ToBigEndian(command, &tx_buffer[0]);
    uint16ToBigEndian(param, &tx_buffer[2]);
    
    // 计算参数的CRC校验
    tx_buffer[4] = calcCRC8(&tx_buffer[2], 2);
    
    esp_err_t result;
    for (uint8_t retry = 0; retry < max_retries; retry++) {
        result = i2c_master_transmit(i2c_device_, tx_buffer, sizeof(tx_buffer), timeout);
        if (result == ESP_OK) {
            return ESP_OK;
        }
        
        ESP_LOGE(TAG, "I2C transmit with param failed, retry %d/%d: %d", retry + 1, max_retries, result);
        // 重试前短暂延迟
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    
    return result; // 返回最后一次错误
}

esp_err_t SCD4X::sendCommandAndRead(uint16_t command, uint8_t* data, size_t data_len, uint32_t timeout, uint8_t max_retries) {
    if (!data) {
        ESP_LOGE(TAG, "Invalid data buffer pointer");
        return ESP_ERR_INVALID_ARG;
    }
    
    esp_err_t result;
    for (uint8_t retry = 0; retry < max_retries; retry++) {
        // 先发送命令
        result = sendCommand(command, timeout, 1); // 这里只尝试一次命令发送
        if (result != ESP_OK) {
            ESP_LOGE(TAG, "Command send failed, retry %d/%d: %d", retry + 1, max_retries, result);
            vTaskDelay(pdMS_TO_TICKS(10));
            continue;
        }
        
        // 发送命令后需要短暂延迟
        vTaskDelay(pdMS_TO_TICKS(2));
        
        // 然后读取数据
        result = i2c_master_receive(i2c_device_, data, data_len, timeout);
        if (result == ESP_OK) {
            return ESP_OK;
        }
        
        ESP_LOGE(TAG, "I2C receive failed, retry %d/%d: %d", retry + 1, max_retries, result);
        // 重试前短暂延迟
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    
    return result; // 返回最后一次错误
}

void SCD4X::uint16ToBigEndian(uint16_t value, uint8_t* buffer) {
    buffer[0] = (value >> 8) & 0xFF;
    buffer[1] = value & 0xFF;
}

uint16_t SCD4X::bigEndianToUint16(const uint8_t* buffer) {
    return ((uint16_t)buffer[0] << 8) | buffer[1];
}

uint8_t SCD4X::calcCRC8(const uint8_t* data, uint8_t len) {
    // CRC-8校验多项式: x^8 + x^5 + x^4 + 1 (0x31)
    uint8_t crc = 0xFF;
    
    for (uint8_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x80) {
                crc = (crc << 1) ^ 0x31;
            } else {
                crc = (crc << 1);
            }
        }
    }
    
    return crc;
}

bool SCD4X::verifyCRC(const uint8_t* data, uint8_t len) {
    // 检查数据长度是否包含CRC字节
    if (len < 2) return false;
    
    // 计算CRC并与数据中的CRC字节比较
    uint8_t calculated_crc = calcCRC8(data, len - 1);
    uint8_t received_crc = data[len - 1];
    
    return (calculated_crc == received_crc);
}